#include <Math.h>
#include "ExPrim.h"

//  DLL standard entry/exit points

HINSTANCE hDLLInst;

BOOL WINAPI DllMain(
                    HINSTANCE hinstDLL,   // handle to DLL module
                    DWORD     fdwReason,  // reason for calling function
                    LPVOID    lpvReserved // reserved
)
{
   hDLLInst = hinstDLL;
   return TRUE;
}


// Definition of callback functions.

ACCURRPROC  pACCurrent;
DCCURRPROC  pDCCurrent;
MSGPROC     pfuncMessage;
PEEKPROCEX  pPeekEx;
POKEPROCEX  pPokeEx;
TRCONDPROC  pTrConduct;
TRREACTPROC pTrReact;
VOLTAGEPROC pVoltage;

double g_dVoltErr;
float  g_fMaxAdmit,
       g_fMinAdmit,
       g_fTemp,
       g_fTempRel,
       g_fVtVolt;


void InitCapacitor (icap* pCap)
{
   pCap->yc   = g_fMinAdmit;
   pCap->ic   = 0.0;
   pCap->time = 0;
}


void InitInductor (iind* pInd)
{
   pInd->yl   = g_fMaxAdmit;
   pInd->il   = 0;
   pInd->time = 0;
}


#define MAX_X_EXP 60


///////////////////////////////////////////////////////////////////////////////
//
// Function:   FieldEffect
//
// Overview:   Computes the field effect and fills the output structure.
//
//             Model equation:
//      
//             Id = If(Vds,Vgs)     ... for Vds ò 0 (normal mode)
//             Id = -If(-Vds,Vgd)   ... for Vds < 0 (inverted mode)
//
//             where: If(Vd,Vg) is:
//             for Vg < Vto:           If = 0
//             for Vto ó Vg ó Vd+Vto:  If = beta * (1+lambda*Vd) * (Vg-Vto)ý
//             for Vd + Vto < Vg:      If = beta * (1+lambda*Vd) * Vd * (2*(Vg-Vto)-Vd)
//
//             Linear approximation at v_ds, v_gs, v_gd:
//             Id =  a + b * Vds + c * Vgs    ...(normal mode)
//             Id = -a + b * Vds - c * Vgd    ...(inverted mode)
//
///////////////////////////////////////////////////////////////////////////////

void FieldEffect (double v_ds, double v_gs, double v_gd, float vto, float beta,
                  float lambda, int iPolarity, ifp* fptr)
{  
   double vgsm, vdsk, dvds, dvgs, a, b, c;

   v_ds *= iPolarity;
   v_gs *= iPolarity;
   v_gd *= iPolarity;
  
   fptr->a = fptr->b = fptr->c = 0.0;
   fptr->rev = 0;
  
   if (v_ds < 0)
   {
      v_ds = -v_ds;
      a = v_gs; v_gs = v_gd; v_gd = a;
      fptr->rev = 1;
   }

   if (v_ds > 100.0)
      v_ds = 100.0;
  
   if (v_gs < -100.0)
      v_gs = -100.0;
   else
      if (v_gs > 100.0)
         v_gs = 100.0;
    
   if ((vgsm = v_gs - vto) > 0.0)
   {
      if (vgsm <= v_ds)
      {
         a = -beta * vgsm * (v_gs + vto + 2.0 * lambda * v_ds * v_gs);
         b = beta * lambda * vgsm * vgsm ;
         c = 2.0 * beta * vgsm * (1.0 + lambda * v_ds) ;
      }
      else
      {
         vdsk = beta * v_ds * (1.0 + lambda * v_ds);
         dvds = beta * (1.0 + 2.0 * lambda * v_ds) * (2.0 * vgsm - v_ds) - vdsk;
         dvgs = 2 * vdsk;
         a = vdsk * (2.0 * vgsm - v_ds) - dvds * v_ds - dvgs * v_gs ;
         b = dvds;
         c = dvgs;
      }
    
      fptr->a = (float)a;
      fptr->b = (float)b;
      fptr->c = (float)c;
   }
}


///////////////////////////////////////////////////////////////////////////////
//
// Function:   InternalDiode
//
// Overview:   Builds simulations of internal diode.
//             Model Equation:         I(U) = is * (exp(U/vt) - 1)
//             Linear Approx. at u=v:  i(u) = yn * u + in
//             where:		   yn = I'(v) = is/vt * exp(v/vt)
//                            in = i(0) = is * ((1 - v/vt) * exp(v/vt) - 1)
//
///////////////////////////////////////////////////////////////////////////////

void InternalDiode (float is, float vk, float vt, double v, idp* dptr)
{
   double ix,
          vx;

   if (v > vk)
   {
      ix = v * dptr->yn + dptr->in;
      
      if (ix > is)
      {
         v = log(ix / is + 1.0) * vt;
         
         if (v < vk)
            v = vk;
      }
   }
  
   vx = v / vt;
  
   if (vx > MAX_X_EXP)
      vx = MAX_X_EXP;
  
   ix       = is * exp(vx);
   dptr->yn = ix / vt;
   dptr->in = ix * (1.0 - vx) - is;
}
	  

///////////////////////////////////////////////////////////////////////////////
//
// Function:   InternalReverceDiode
//
// Overview:   Builds simulations of internal diode.
//             Model Equation:         I(U) = - ibv * exp((-bv-U)/vt)
//             Linear Approx. at u=v:  i(u) = yb * u + ib
//             where:		     yb = I'(v) = ibv/vt * exp((-bv-v)/vt)
//                              ib = i(0) = - ibv * (1 + v/vt) * exp((-bv-v)/vt)
//
///////////////////////////////////////////////////////////////////////////////

void InternalReverceDiode (float ibv, float bv, float vk, float vt, double v, irdp *dptr)
{
   double ix, vx;
  
   if (v >= 0)
   {
      dptr->ib = dptr->yb = 0.0;
      return;
   }
  
   if (v < vk)
   {
      ix = v * dptr->yb + dptr->ib;
    
      if (-ix > ibv)
      {
         v = - log(-ix / ibv) * vt - bv;
      
         if (v > vk)
            v = vk;
      }
   }
  
   vx = (-bv - v) / vt;
   
   if (vx > MAX_X_EXP)
      vx = MAX_X_EXP;
  
   ix       = ibv * exp(vx);
   dptr->ib = - ix * (1.0 + v / vt);
   dptr->yb = ix / vt;
}


void MaxErr (int iNet1, int iNet2, double* pV)
{
  double dVolt = VOLTAGE(iNet1, iNet2),
         dErr  = fabs(dVolt - *pV);

  if (dErr > g_dVoltErr)
     g_dVoltErr = dErr;
  *pV = dVolt;
}


void MaxErrEx (int iNet1, int iNet2, double* pV, double* pdVoltErr)
{
  double dVolt = VOLTAGE(iNet1, iNet2),
         dErr  = fabs(dVolt - *pV);

  if (dErr > *pdVoltErr)
     *pdVoltErr = dErr;
  *pV = dVolt;
}


float PN_Capacitor (float Cjo, float TT, float M, float FC, float VJ, float Is, float Ibv, float Bv, float volt)
{
   float Cj,
         di_dt;

   SET_RANGE(volt, (float)(-Bv - 2.0), (float)2.0);
   
   if (volt > FC * VJ)
      Cj = (float)(Cjo * pow(1 - FC, -(M + 1)) * (1 - FC * (M + 1) + M * volt / VJ));
   else
      Cj = (float)(Cjo * pow(1 - volt / VJ, -M));
  
   if (TT < 1e-18)
      return Cj;

   if (volt > 0.0)
      di_dt = (float)(Is * exp(volt / g_fVtVolt) / g_fVtVolt);
   else
      di_dt = (float)(Ibv * exp((-Bv - volt) / g_fVtVolt) / g_fVtVolt);
  
   return Cj + TT * di_dt;
}


__declspec(dllexport)
void __stdcall SetAngCallback (TRCONDPROC pTrConductProc,
   TRREACTPROC pTrReactProc, DCCURRPROC pDCCurrentProc,
   ACCURRPROC pACCurrentProc, VOLTAGEPROC pVoltageProc, MSGPROC pMsg)
{
   pTrConduct   = pTrConductProc;
   pTrReact     = pTrReactProc;
   pDCCurrent   = pDCCurrentProc;
   pACCurrent   = pACCurrentProc;
   pVoltage     = pVoltageProc;
   pfuncMessage = pMsg;
}

__declspec(dllexport)
void __stdcall SetAngSimParam (float fMinAdmit, float fMaxAdmit, float fTemp)
{
   g_fMinAdmit = fMinAdmit;
   g_fMaxAdmit = fMaxAdmit;
   g_fTemp     = fTemp;
}


void SetCapacitor (float fVolt, icap* pCap)
{
   pCap->yc   = g_fMaxAdmit;
   pCap->ic   = g_fMaxAdmit * fVolt;
   pCap->time = 0;
}


void SetCurrent (int iPlus, int iMinus, double dCurrent)
{
   DC_CURRENT(iMinus, iPlus, dCurrent);
   CONDUCTANCE(iPlus, iMinus, g_fMinAdmit);
}


void SetCapacitorAC (int n1, int n2, float fCap, float fOmega)
{
   REACTANCE(n1, n2, fCap * fOmega);
}


void SetCapacitorDC (int n1, int n2, float fCap, icap* pCap, float fSimTime, WORD wMode)
{
   if (wMode & INTEGRATE)
   {
      pCap->yc   = (fSimTime - pCap->time > fabs(fCap) / g_fMaxAdmit) ? ((double)fCap / (fSimTime - pCap->time)) : g_fMaxAdmit;
      pCap->ic   = VOLTAGE(n1, n2) * pCap->yc;
      pCap->time = fSimTime;
   }
   DC_CURRENT(n2, n1, pCap->ic);
   CONDUCTANCE(n1, n2, pCap->yc);
}


void SetInductor (float fCurrent, iind* pInd)
{
   pInd->yl   = 0; 
   pInd->il   = fCurrent;
   pInd->time = 0;
}


void SetInductorAC (int n1, int n2, float fInd, float fOmega)
{
   if (fInd >= 1e-18)
      REACTANCE(n1, n2, -1 / (fInd * fOmega));
   else
      CONDUCTANCE(n1, n2, g_fMaxAdmit);
}


void SetInductorDC (int n1, int n2, float fInd, iind* pInd, float fSimTime, WORD wMode)
{
   if (fabs(fInd) < 1e-18)
   {
      CONDUCTANCE(n1, n2, g_fMaxAdmit);
      return;
   }
   if (wMode & INTEGRATE)
   {
      pInd->il   += VOLTAGE(n1, n2) * pInd->yl;
      pInd->yl    = (fabs(fInd) > (fSimTime - pInd->time) / g_fMaxAdmit)? ((double)(fSimTime - pInd->time) / fInd) : g_fMaxAdmit;
      pInd->time  = fSimTime;
   }
   DC_CURRENT(n1, n2, pInd->il);
   CONDUCTANCE(n1, n2, pInd->yl);
}


void __stdcall SetLogCallbackEx (PEEKPROCEX pPeek, POKEPROCEX pPoke, MSGPROC pMsg)
{
   pPeekEx      = pPeek;
   pPokeEx      = pPoke;
   pfuncMessage = pMsg;
}


void SetMutIndAC (int p1, int p2, int s1, int s2, float f, float fOmega)
{
   if (f < 1e-18)
      return;
   TRANSREACTANCE(p1, p2, s2, s1, -1 / (f * fOmega));
}


void SetMutIndDC (int p1, int p2, int s1, int s2, float f, iind* pIind, float fSimTime, WORD wMode)
{
   if (fabs(f) < 1e-18)
      return;
   if (wMode & INTEGRATE)
   {
      pIind->il   += VOLTAGE(p1, p2) * pIind->yl;
      pIind->yl    = (fabs(f) > (fSimTime - pIind->time) / g_fMaxAdmit) ? ((double)(fSimTime - pIind->time) / f) : g_fMaxAdmit;
      pIind->time  = fSimTime;
   }
   DC_CURRENT(s2, s1, pIind->il);
   TRANSCONDUCTANCE(p1, p2, s2, s1, pIind->yl);
}


void SetVoltage (int iPlus, int iMinus, double dVolt)
{
   DC_CURRENT(iMinus, iPlus, dVolt * g_fMaxAdmit);
   CONDUCTANCE(iPlus, iMinus, g_fMaxAdmit);
}


void SetTemp ()
{
   g_fTempRel = (273 + g_fTemp) / 300;
   g_fVtVolt  = (float)(0.02585 * g_fTempRel);
}


// SEMICONDUCTOR DEVICE TEMPERATURE DEPENDENCIES.

float Temp_brf (float brf, float xtb)
{
   if (brf < 1e-12)
      brf = (float)1e-12;
   return (float)(brf * pow(g_fTempRel, xtb));
}


float Temp_cj (float cjo, float m, float vj_div)
{
   return (float)(cjo * (1 + m * (.0004 * (g_fTemp - 27) + (1 - vj_div))));
}


float Temp_is (float is, float n, float eg, float xti)
{
   if (is < 1e-30)
      is = (float)1e-30;
   return (float)(is * exp((g_fTempRel-1) * eg / (n * g_fVtVolt)) * pow(g_fTempRel, xti / n));
}


float Temp_vj (float vj)
{
   return (float)(vj * g_fTempRel - 3.0 * g_fVtVolt * log(g_fTempRel));
}


void TestResist (float fRes, float* pfMinAdmit, float* pfMaxAdmit)
{
   fRes = (float)fabs(fRes);
   if (fRes < (float)1e-6)
      return;
   else
      fRes = 1 / fRes;
   if (fRes > *pfMaxAdmit)
      *pfMaxAdmit = fRes;
   if (fRes < *pfMinAdmit)
      *pfMinAdmit = fRes;
}
